/**
 * 
 */
package strain.solver;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import strain.domain.Domain;
import strain.domain.InconsistentDomainException;
import strain.expression.Expression;

public class SolverState{
    
    private final Map<Integer, Expression<?,?>> expressions = new HashMap<Integer, Expression<?,?>>();
    
    public <T, D extends Domain<T>> void add(final Expression<T, D> expression){
        this.expressions.put(expression.getId(), expression);
    }
    
    @SuppressWarnings("unchecked")
    public <T, D extends Domain<T>> Expression<T,D> get(final Expression<T, D> expression){
        return (Expression<T, D>) this.expressions.get(expression.getId());
    }
    
    public Collection<Expression<?,?>> getExpressions(){
        return Collections.unmodifiableCollection(this.expressions.values());
    }
    
    public boolean isBound(){
        for(Expression<?,?> e: this.expressions.values()){
            if (!e.isBound())
                return false;
        }
        return true;
    }
    
    public void reduceDomains() throws InconsistentDomainException{
        // this may be replaced by some kind of queue that keeps track of expressions that have
        // been reduced, and therefore need to propagate their reductions
        
        // keep in mind expressions whos sub-expressions have been reduced, can we get to them?
        
        // what about policies leading to failing faster?  or propagating faster?
        // propagate on most constrained or smallest domain first?
        while(domainReductionPass()){}
    }
    
    private boolean domainReductionPass() throws InconsistentDomainException{
        boolean result = false;
        for(Expression<?,?> e: this.expressions.values()){
            result = result || e.propagate();
        }
        return result;
    }
    
    public SolverState copy(){
        SolverState result = new SolverState();
        for(Expression<?,?> expression: this.expressions.values()) expression.addTo(result);
        return result;
    }
    
}